var selection 	= new Object;
var state 		= new Object;
var properties	= new Object;
var builder		= new Object;
var dragDrop	= { activated: false, target: null, dragging: false, handle: $('<div>', { 'id': 'dragHandle' }), closeTimer: null };

var objColourPicker;

var viewerOnly = false;

// A user can click the cell & the result and it will act the same as if they have dragged. These store the currently clicked cells.
selection.Cell = false;
selection.Result = false;

state.chartDraggable = false;
state.overlayMode = false;

properties.panel = 'search';

function copyImageToCell(source, target){
	//this = target; ui.draggable = src;
	containerHeight = parseFloat($(target).outerHeight() - $(target).children('.ctext, .ctbottom').outerHeight() - 6);
	resizeCimg(target);

	// Has the image come from the users personal library?
	if($(source).hasClass('personalPhotoContainer')){

		$(target).data('cellObject').image = $(source).data('imageURL');
		$(target).data('cellObject').imageID = $(source).data('imgID');
		
		var url = $(source).data('imageURL');
		var tag = browserDependentTag(url);
		var tagName = tag.get(0).tagName.toLowerCase();
		
		if( /img/i.test(tagName) ) $(tag).hide();
		
		$(target).children('.cimg').html( 
			$(tag).attr({ 'src': browserDependentImage(url), 'alt': $(source).data('name') }).load(function(){
				// Resize the image.
				resizeImage($(target).find('img, embed'), $(target).children('.cimg'));
			})
		);	
		
		// Check to see if there are any embed tags, we'll need to bind the loading code to them
		if( /embed/i.test(tagName) ) bindEmbedLoad();	

	} else {
		
		// Image type handling based on browser
		var fsrc = getStrStrImage(source);
		var tag = browserDependentTag(fsrc);
		var tagName = tag.get(0).tagName.toLowerCase();
		
		if( /img/i.test(tagName) ) $(tag).hide();				
		
		$(target).children('.cimg').html( 
			$(tag).attr({ 'src': browserDependentImage(fsrc), 'alt': $(source).data('name') } ).load(function(){
				// Resize the image.
				resizeImage($(target).find('img, embed'), $(target).children('.cimg'));
			})
		);
		
		// Check to see if there are any embed tags, we'll need to bind the loading code to them
		if( /embed/i.test(tagName) ) bindEmbedLoad();		

		$(target).data('cellObject').image = fsrc;
		$(target).data('cellObject').imageID = 0;
	
		// Update the cell text based on the image if it's not been set already.
		if($(target).find('.textHolder').text() == ""){
			updateCellText(target, $(source).data('name'));
		}
	
	}
	
	state.unsaved = true;
}

function getStrStrImage(source){
	var SVGsrc = $(source).data('imageSVGURL');
	var WMFsrc = $(source).data('imageWMFURL');
	var PNGsrc = $(source).data('imagePNGURL');
	
	var src = 
		$.browser.msie ? (
			WMFsrc ? WMFsrc : (PNGsrc ? PNGsrc : false) ) : (
			SVGsrc ? SVGsrc : (PNGsrc ? PNGsrc : false) );
	return src;
}

function resizeCimg(target){
	// Resize cimg so it fits inside .cell
	containerHeight = parseFloat($(target).height() - $(target).children('.ctext, .ctbottom').outerHeight());
	//console.log('resizeCimg trigger, setting cimg to ' + containerHeight);
	$(target).children('.cimg').height(containerHeight);
}

function bindEmbedLoad(){
	embedElements = document.getElementsByTagName('embed');
		
	if(embedElements.length > 0){
		for(i=0;i<embedElements.length;i++){
			// Attach selectables for every embed when it loads with the SVG context.
			embedElements[i].onload = function(){
				attachSelectableCellsWithSelector( this.getSVGDocument(), this.parentNode );
				resizeImage($(this.parentNode).find('img, embed'), $(this.parentNode));
			};
		}
	}
}

function updateCellText(target, text){
	if(text == "") return;
	$(target).data('cellObject').text = text;
	
	$(target).find(".ctext, .ctbottom").children('.textHolder').html(text);
	checkTextArea($(target).find(".ctext, .ctbottom"));
	
}

function attachSelectables(){
	// Bind click for chart
	$('#PBchartNameParentHolder').bind('click', function(e){
		try{
			if(e.originalEvent.toElement.className == "zoomLink") return;
		} catch(e){
		
		}
		$(this).parent().addClass('cellHover');	
		$('.cell').removeClass('cellHover');

		// Show the properties pane for the chart.
		if(properties.panel == "prop") populatePropertiesPane(this);
	});
	
	bindEmbedLoad();
	
	attachSelectableCellsWithSelector('.cimg, .searchResult, .ctext', null);

}

function attachSelectableCellsWithSelector(sel, parent){
	// Bind click for cells and search results
	$(sel).bind('click', function(e){
		// Are we in viewer mode?
		if(viewerOnly) return false;
		
		// Remove hover from table.
		$('#chartHolder').removeClass('cellHover');
		
		//console.dir(parent);
		target = (parent == null) ? this : parent;
		
		if($(target).hasClass('ctext')){
			// The user has selected the text area in the cell.
			target = $(target).siblings('.cimg');
			selectCell(target);

			// Show the properties pane for the cell.
			if(properties.panel == "prop") populatePropertiesPane(target);			
		} else if( $(target).hasClass('cimg')){
			// The user has selected the cell.
			selectCell(target);
			
			// Show the properties pane for the cell.
			if(properties.panel == "prop") populatePropertiesPane(target);
			
		} else if($(target).hasClass('searchResult')){
			// The user has selected a search result. Has the user previously selected a result? If so, replace selection with this one.
			if($('.searchResult.cellHover').length == 0){
				// No current selection.
				selection.Result = $(target).attr('id');
				$(target).addClass('cellHover');
			} else {
				// Is the current result selected? We should deselect it if so.
				if($(target).hasClass('cellHover')){
					$(target).removeClass('cellHover');
					selection.Result = false;
				} else {
					$('.searchResult.cellHover').removeClass('cellHover');
					selection.Result = $(target).attr('id');
					$(target).addClass('cellHover');
				}
			}
		}
		
		//console.dir(selection);
		
		// OK, click detection / selection done. Do we have a match?
		if(selection.Result != false && selection.Cell != false){
			// Yes, Do a copy, and reset selected variables back to nothing.
			copyImageToCell($("#" + selection.Result), $("#" + selection.Cell).parent());
			// Reset selection variables to false.
			selection.Result = false;
			selection.Cell = false;
			// Remove the hover classes.
			$('.cellHover').removeClass('cellHover');
		}
	});
}

function selectCell(target){
	target = $(target);
	if(target.hasClass('cell')) target = target.find('.cimg');
	$('#chartHolder').removeClass('cellHover');
	
	$('.cell.cellHover').removeClass('cellHover');
	selection.Cell = $(target).attr('id');
	$(target).parent().addClass('cellHover');
}

function getVisibleCells(){
	return $('.cell:lt(' + properties.activeCells + ')');
}

function attachResize(){
	// Resizing text area
	attachResizeWithSelector( getVisibleCells() );

}

function attachResizeWithSelector(selector){
	// Destroy if already created.
	$( selector ).find('.ctext, .ctbottom').each(function(i){
		if(typeof $(this).data('resizable') != "undefined") $(this).resizable('destroy');
	});
	
	$( selector ).children('.ctext').resizable({ 
		handles: 's',
		minHeight: 20,
		containment: 'parent',
		start: function(event, ui){
			$(this).resizable('option', 'maxHeight', $(this).parent().height() / 1.75);
			var minHeight = $(this).children('.textHolder').height() == 0 ? 27 : $(this).children('.textHolder').height();
			$(this).resizable('option', 'minHeight', minHeight);
		},
		resize: function(event, ui) {
			
			// Resize the container div to fit the new resized element
			var newHeight = ($(this).parent().height()) - $(this).outerHeight();

			// Set the new height to the cell object
			$(this).parent().data('cellObject').textHeight = $(this).height();

			// Resize the image container
			$(this).siblings('.cimg').height(newHeight);
			
			// Pointer to the image
			image = $(this).siblings('.cimg').children('img, embed');
			
			// Is there an image present in the cell?
			if(image.length == 1){
				resizeImage(image, $(this).siblings(".cimg"));		
			}
			
			state.unsaved = true;
		},
		stop: function(event, ui){
			
			// We need to edit the integer control if its present.
			var intControl = $('.intAnchor.textHeight').children('span');
	
			//console.dir(intControl);
	
			if(intControl.length > 0){
				intControl.data('intControl').setVal( $(this).parent().data('cellObject').textHeight );
			}
		}

	});
	
	// Bottom aligned text
	$( selector ).children('.ctbottom').resizable({ 
		handles: 'n',
		minHeight: 20,
		start: function(event, ui){
			$(this).resizable('option', 'maxHeight', $(this).parent().height() / 1.75);
			var minHeight = $(this).children('.textHolder').height() == 0 ? 27 : $(this).children('.textHolder').height();
			$(this).resizable('option', 'minHeight', minHeight);
		},
		resize: function(event, ui) {
			$(this).resizable('option', 'maxHeight', $(this).parent().height() / 1.75);
			$(this).css('bottom', '0px');
			$(this).css('top', '0px');
			// Resize the container div to fit the new resized element
			var newHeight = ($(this).parent().height()) - $(this).outerHeight();

			$(this).parent().data('cellObject').textHeight = $(this).height();

			$(this).siblings('.cimg').height(newHeight);
			
			// Pointer to the image
			image = $(this).siblings('.cimg').children('img, embed');
			
			// Is there an image present in the cell?
			if(image.length == 1){
				resizeImage(image, $(this).siblings(".cimg"));
			}

			state.unsaved = true;
		},
		stop: function(event, ui){
			
			// We need to edit the integer control if its present.
			var intControl = $('.intAnchor.textHeight').children('span');
	
			//console.dir(intControl);
	
			if(intControl.length > 0){
				intControl.data('intControl').setVal( $(this).parent().data('cellObject').textHeight );
			}
		}

	});
}

function resizeImage(imgPointer, container, cb){
	
	// Recalculate image height & width
	origHeight 	= (imgPointer.height() == 0) ? imgPointer.attr('height') : imgPointer.height();
	origWidth 	= (imgPointer.width() == 0) ? imgPointer.attr('width') : imgPointer.width();

	if(typeof $(container).parent().data("originalDimensions") == "undefined") {

		if(origHeight == 0 && origWidth == 0){
			setTimeout(function(){ resizeImage(imgPointer, container) }, 500);
			return;
		}
		$(container).parent().data("originalDimensions", { "height": origHeight, "width": origWidth });

	}

	baseHeight 	= $(container).parent().data("originalDimensions").height;
	baseWidth 	= $(container).parent().data("originalDimensions").width;

	//$(container).parent().removeData("originalDimensions");

	/*
	console.log("Start height: "+origHeight);
	console.log("Start width: "+origWidth);

	console.log("container height: "+$(container).height());
	console.log("container width: "+$(container).width());

	
	console.log("Original height: " + baseHeight);
	console.log("Original width: " + baseWidth);
	*/
	
	// Exclude SVG's from sizing if they are included in an IMG

	imgTmp = imgPointer.attr('src').match(PicBoard.regex.extension);
	//console.log(imgPointer[0].tagName);
	if(imgTmp[1].toLowerCase() == "svg" && imgPointer[0].tagName.toLowerCase() == "img") {
		imgPointer.fadeIn('slow');
		if($.browser.mozilla) imgPointer.css('display', 'inline');
		//console.log("height:" + imgPointer.height());
		return;
		//if(/straight-street/i.test(imgPointer.attr('src'))) return;
	}
	
	if(origWidth != $(container).width() || origHeight != $(container).height()) {
		
		xRatio = $(container).height() / baseHeight;
		yRatio = $(container).width() / baseWidth;
		
		//console.log("x ratio: " + xRatio);
		//console.log("y ratio: " + yRatio);
		var cell = $(container).parent();
		var border = { "y": getBorder(cell, "y"), "x": getBorder(cell, "x") };
		
		// Image is smaller
		if(xRatio < 1 && yRatio < 1) {
			if(xRatio > yRatio) {
				imgPointer.height( (baseHeight * yRatio) - border.y);
				imgPointer.width( (baseWidth * yRatio) - border.x);
			} else if(yRatio > xRatio) {
				imgPointer.height( (baseHeight * xRatio) - border.y);
				imgPointer.width( (baseWidth * xRatio) - border.x);
			}
		} else {
			if(xRatio > yRatio) {
				imgPointer.height( (baseHeight * yRatio) - border.y);
				imgPointer.width( (baseWidth * yRatio) - border.x);
			} else if(yRatio > xRatio) {
				imgPointer.height( (baseHeight * xRatio) - border.y);
				imgPointer.width( (baseWidth * xRatio) - border.x);
			}
		}
		
		if(xRatio > yRatio) {
			imgPointer.height( (baseHeight * yRatio) - border.y);
			imgPointer.width( (baseWidth * yRatio) - border.x);
		} else if(yRatio > xRatio) {
			imgPointer.height( (baseHeight * xRatio) - border.y);
			imgPointer.width( (baseWidth * xRatio) - border.x);
		}
		
	}

	// Show the image if it's an img tag.
	if(imgPointer[0].tagName.toLowerCase() == "img") imgPointer.fadeIn('slow');
	if($.browser.mozilla) imgPointer.css('display', 'inline');
	
	
	if(cb != null) cb();
}

function attachRenameTextWithSelector(selector){
	// Doubleclick handler for cells for renaming text
	$(selector).bind('dblclick', function(e){
		// Re-enable selection on the cells.
		$('.cell').enableSelection();
	
		// Replace line breaks with literal character.
		data = $(this).children('.textHolder').text();
		data = data.replace(/<br*.?>/g, '\n');
		
		var boxID = $(this).attr('id') + 'box';
		
		$(this).children('.textHolder').replaceWith( $("<textarea>").attr({'class': 'ctextbox', 'id': boxID }).css({'height': $(this).height() - 7, width: $(this).width() - 7 }).text( data ) );
		
		
		boxID = "#" + boxID;
		
		$(boxID).focus();	
		$(boxID).select();
		
		// When the textbox loses focus, change back to a div.
		$(boxID).bind('focusout', function(e){
			// Parse newlines to br's so they show up.
			data = $(this).val();
			data = data.replace(/\n/g, '<br />');
			id = "#" + $(this).parent().attr('id');

			$(this).replaceWith( $("<div>", { html: data }).attr({ 'class': 'textHolder' }) );
			
			// Store data to the cell.
			$(id).parent().data('cellObject').text = data;
			populatePropertiesPane($( id ));
			
			checkTextArea(id);

			state.unsaved = true;
			$('.cell').disableSelection();
		});
		
		$(boxID).bind('keyup', function(event){
			if (event.keyCode == 13) {
				$(this).val( $(this).val().replace(/\n/g, '') );
				$(this).trigger('focusout');
				
				$('#propTable input#txttext').val(data);
				
			}
		});			
		
	});
}

function attachRenameText(){
	attachRenameTextWithSelector('.ctext, .ctbottom');
}

function checkTextArea(target){
	// Check to see if we're overflowing the text area.
	var th = parseInt($( target ).find('.textHolder').height() + getMargin(target, "y"));
	var ph = parseInt($( target ).outerHeight());
	
	if( th > ph ){
		$( target ).height( parseInt($( target ).find('.textHolder').height()) );
		
		// Resize cimg
		resizeCimg( $( target ).parent() );
		
		f_img = image = $( target ).parent().find('embed, img');
		
		if(f_img.length > 0) resizeImage(f_img, $( target ).siblings('.cimg') );
	}
}

function attachRenameChart(){

	PicBoard.getTranslations(['lang_84_edit'], function(){
		$('#PBchartNameHolder').after($("<button>", { 
			id: 'chartEditButton',
			text: PicBoard.languageMap.lang_84_edit,
			click: function(){
				$('#PBchartNameHolder').trigger('dblclick');
			}
		}));
		
		$('#chartEditButton').button();

	});

	// Doubleclick handler for chart for renaming chart.
	$('#PBchartNameHolder').bind('dblclick, click', function(e){
		data = $(this).text();
		
		$(this).html( $("<input/>", { id: $(this).attr('id') + 'box' }).val( data ));
		
		$('#' + $(this).attr('id') + 'box').focus();
		
		
		$('#' + $(this).attr('id') + 'box').bind('keyup', function(event){
			if(event.keyCode == 13){
				$(this).trigger('focusout');
			}
		});
		
		$("#" + $(this).attr('id') + 'box').bind('focusout', function(e){
			if($(this).val() != ""){
				data = $(this).val();
				$('#PBchartNameHolder').html( data );
				$('#chartHolder').data('chartObject').title = data;

				state.unsaved = true;
			}
		});
	});
}

function populatePropertiesPane(target){
	if($(target).hasClass('cimg')){
		
		var dataSource = $(target).parent().data('cellObject');
	
		propertiesPane = dataSource.displayProperties();
		$('#propertiesContent').html( "" );
		$('#propertiesContent').html( propertiesPane );
	
		$(".radioset").buttonset();
	
		// IE fix for event-bubbling failings
		$('.radioset > label > span > img').bind('click', function(){
			// Change the button state
			$(this).parent().parent().trigger('click');
			// Trigger the actual event (handled below)
			$(this).parent().parent().siblings('input#' + $(this).parent().parent().attr('for') + '[type=radio]').trigger('click');
		});

		// Auto bindings
		
		// Text
		$('#propTable input#txttext').bind('keyup', function(){
			$(target).siblings(".ctext, .ctbottom").children('.textHolder').html($('#propTable').find('input#txttext').val());
			//attachResize(); - textHolder should negate the need for this.
			dataSource.text = $('#propTable').find('input#txttext').val();
			
			if( parseInt($(target).parent().find('.textHolder').height()) > parseInt($(target).siblings('.ctext, .ctbottom').outerHeight()) ){
				$(target).siblings('.ctext, .ctbottom').height( parseInt($(target).parent().find('.textHolder').height()) );
				
				// Resize cimg
				resizeCimg( $(target).parent() );
				
				f_img = image = $(target).children('embed, img');
				
				if(f_img.length > 0) resizeImage(f_img, $(target) );
			}
		});

		// Text Alignment
		$('input[type=radio][name=radio_textAlignment]').bind('click', function(){
			$(target).siblings('.ctext, .ctbottom').css('text-align', $('input[type=radio][name=radio_textAlignment]:checked').val());
			dataSource.textAlignment = $('input[type=radio][name=radio_textAlignment]:checked').val();
		});
		
		
		// Text Location
		$('input[type=radio][name=radio_textLocation]').bind('click', function(){
			val = $("input[type=radio][name=radio_textLocation]:checked").val();

			if(val != dataSource.textLocation){

				$(target).swap('div#' + $(target).siblings('.ctbottom, .ctext').attr('id'));
				 
				if( val == "bottom" ){
					//$(target).prev().insertAfter(target);
					
					$(target).siblings('.ctext').addClass('ctbottom').removeClass('ctext');
					$(target).siblings('.ctbottom').css({ top:'0px', bottom: '-2px' });
					attachResize();
								
				} else {
					//$(target).insertAfter($(target).next());
					$(target).siblings('.ctbottom').addClass('ctext').removeClass('ctbottom');
					$(target).siblings('.ctbottom').css({ top:'', bottom: '' });
					attachResize();
				}
				
				dataSource.textLocation = val;
			}
		});
		

		// Font
		$('#PBCellfontPicker').bind('change', function(){
			var fontVal = $(this).val();
			$(target).siblings('.ctext, .ctbottom').css('font-family', fontVal);
			dataSource.fontFace = fontVal;
		});

		// Bind the font option buttons to handler.
		$(".PBTextSetting").each(function(index){
			$(this).data('enabled', $(target).parent().data('cellObject')[$(this).attr('id')]);
			$(this).data('enabled') ? $(this).children('div').addClass('selectedButton') : $(this).children('div').removeClass('selectedButton');
			
			$(this).bind('click', function(){
				$(this).data('enabled', $(this).data('enabled') ? false : true);
				$(this).data('enabled') ? $(this).children('div').addClass('selectedButton') : $(this).children('div').removeClass('selectedButton');
			});
		});

		// Font options
		$('.PBTextSetting').bind('click', function(){

			var val = $(this).data('enabled');
			
			// Write changes to the cell Object.
			dataSource[$(this).attr('id')] = val;
			
			// Apply changes inline.
			if($(this).attr('id') == "PBCbold"){
				(val) ? $(target).parent().css('font-weight', 'bold') : $(target).parent().css('font-weight', 'normal');
			}
			
			if($(this).attr('id') == "PBCitalic"){
				(val) ? $(target).parent().css('font-style', 'italic') : $(target).parent().css('font-style', 'normal');
			}
			
			if($(this).attr('id') == "PBCunderline"){
				(val) ? $(target).parent().css('text-decoration', 'underline') : $(target).parent().css('text-decoration', 'none');
			}	
		});
		
		// Events
		
		
		// Show the current font in the font picker.
		$('#PBCellfontPicker').children("option").filter(function(index){
			return $(this).val() == dataSource.fontFace;
		}).attr("selected", "selected");
		
		
		// Bind the colour links to colour objects.
		$('.colourAnchor').each(function(i){
			var self = $(this);
			$(this).wrap( $('<a>', { href: '#', click: function(e){ displayColourDialog( $(self).attr('id') ) } }) );
		});
		
		// Switch the integer elements then bind.
		$('.intAnchor').each(function(i){
			
			// Get the old class applicable to.
			var applicableTo = $(this).attr('class').split(' ').slice(-1);
			
			
			// Get the current size.
			var currentSize = dataSource[applicableTo];
			var maxSize = dataSource.properties[applicableTo].max;
			var minSize = dataSource.properties[applicableTo].min;
			var stepping = dataSource.properties[applicableTo].stepping;
			
			// Create the control

			var objIntControl = new numberSelectControl($(this), minSize, currentSize, maxSize);

			// Set the stepping.
			objIntControl.setStepping(stepping);
			
			// Write out to the DOM and get the ID of the new control
			var id = objIntControl.out();
			
			$("#" + id).data('intControl', objIntControl);
			$("#" + id).addClass("intControl " + applicableTo[0]);
			
		});
		
	}
	
	$('.intAnchor a').bind('click', function(){		
		// Get the old class applicable to.
		var applicableTo = $(this).parent().attr('class').split(' ').slice(-1);
		
		// Get the current value
		var size = $(this).siblings('span').data('intControl').current;
		
		// Commit the size to the cell object.
		dataSource[applicableTo] = size;
		
		// Apply size
		switch(applicableTo[0]){
		
			case "textSize":
				// Text size is smaller than the text height 
				$(target).parent().css('font-size', size);				

				// Is size larger than the textHeight? If so we must increase accordingly.
				if( size > parseInt( $('span.intControl.textHeight').text() ) ) {
					$('span.textHeight').children('a').last().trigger('click');
				}
				
				if(size < parseInt($(target).parent().find('.textHolder').height()) ){
					var newSize = parseInt($(target).parent().find('.textHolder').height());
					$(target).siblings('.ctext, .ctbottom').height( newSize );
			
					dataSource['textHeight'] = newSize;
			
					resizeCimg( $(target).parent() );
			
					f_img = image = $(target).children('embed, img');
				
					if(f_img.length > 0) resizeImage(f_img, $(target) );
				}
				
			break;
		
			case "textHeight":
				
				$(target).siblings('.ctext, .ctbottom').height(size);
				$(target).height($(target).parent().height() - size);
				
				// 14 = padding + margin
				$(target).siblings('.ctbottom').css('top', "0px" );
				//console.log('newtop:' + parseInt( $(target).siblings('.ctbottom').css('top') ) );
				//$(target).siblings('.ctbottom').css('bottom', '-5px');
				//$(target).siblings('.ctbottom').css('top', "");
				
				//console.dir($(target).siblings('.ctbottom').attr('style'));
				
				if(size < parseInt( $('span.intControl.textSize').text() ) ){
					$('span.textSize').children('a').first().trigger('click');
				}
				
				dataSource['textHeight'] = size;
				
				resizeCimg( $(target).parent() );
				
				f_img = image = $(target).children('embed, img');
				
				if(f_img.length > 0) resizeImage(f_img, $(target) );				
				
			break;
		
		}
	});
	
	if($(target).parent().attr('id') == 'chartHolder'){
		$('#propertiesContent').html( $($(target).parent().data('chartObject').displayProperties()) );
		
		$('#PBpropApply').bind('click', function(e){
			// Bind the click event for the apply button. Only visual change is the chart title, so lets start with that.
			$('#PBchartNameHolder').text($('#txttitle').val());
			
			$(target).parent().data('chartObject').title = $('#txttitle').val();
			
			$(target).parent().data('chartObject').description = $('#txtdescription').val();
			
			$(target).parent().data('chartObject').visibility = $('#selectvisibility').val();
			
			//console.dir( $(target).parent().data('chartObject') );
			
			state.unsaved = true;
		});
	}
	
}

function displayColourDialog(ref){
	
	getFragment("colourPicker", function(data) {
	    jQuery.facebox(data);

	    objColourPicker = new PBcolourPicker("#PBCP1", "#swatch");
			
		$("#PBCP1").slider({
			orientation: 'horizontal',
			range: "min",
			max: 200,
			value: 0,
			slide: objColourPicker.alterDarkness,
			change: objColourPicker.alterDarkness
		});
		
		$('#PBcolourApply').bind('click', function(e){
			$('#' + ref).html(objColourPicker.getOutputColour() + $('#' + ref).children().outerHTML() );
			
			// What are we applying this to?
			var applicableTo = $('#' + ref).attr('class').split(' ').slice(-1);
			
			var parent = $("#" + selection.Cell).parent();
			
			if(applicableTo == "colour"){
				// Set the colour and commit to the cell Object.
				parent.css('color', objColourPicker.getOutputColour());
				parent.data('cellObject').colour = objColourPicker.getOutputColour();
			}
			if(applicableTo == "bgcolour"){
				// Set the colour and commit to the cell Object.
				parent.css('background-color', objColourPicker.getOutputColour());
				//console.dir($("#" + selection.Cell));
				parent.data('cellObject').bgcolour = objColourPicker.getOutputColour();				
			} 
			if(applicableTo == "frameColour"){
				parent.css('border-color', objColourPicker.getOutputColour())
				parent.data('cellObject').frameColour = objColourPicker.getOutputColour();
			}
		});
	});
}

function copyCellObjects(){
	// Copy the cell object into each of the cells.
	var cellLoc = 1;

	var rowLength = parseInt( ($('#cell1').width() / $('#chartHolder').width()) * getCellMargin("x") );
	
	$('.cell').each(function(i){
		$(this).data('cellObject', jQuery.extend({}, PicBoard.cellObj) );
		
		// Overlay mode will provide us with gridsize information
		if(state.overlayMode){
			var columns = $('#chartHolder').data('overlayObject').columns;
			// Work out what cell we are - x,y.
			y = Math.ceil(cellLoc / columns);
			x = Math.ceil(cellLoc - y*columns + columns);
		} else {
			// Work out what cell we are - x,y.
			y = Math.ceil(cellLoc / rowLength);
			x = Math.ceil(cellLoc - y*rowLength + rowLength);
		}
		$(this).data('cellObject').gridRef = [x, y];
		
		cellLoc++;
	});
}

function copyChartObject(){
	//console.log('write init object');
	$('#chartHolder').data('chartObject', jQuery.extend({}, PicBoard.chartObj) );
	if(typeof templateData != "undefined") $('#chartHolder').data('overlayObject', templateData);
}

function setupCellNavLinks(){
	// Add hover tooltips
	$('#CellcontrolHolder > a[id]').tipsy({gravity: 's'});
	
	// Bind the properties link.
	$('#cell_propertiesLink').bind('click', function(){
		if(properties.panel == "prop") return;
		
		if((typeof properties.propertiesElement) != "undefined"){
			hideAllPanelsBut("PBPropertiesPane");
			
			$('#messagePane').after(properties.propertiesElement);
			populatePropertiesPane($("#" + selection.Cell));
		} else {
			// Get the template from the server.
			getFragment("properties", function(data){
				hideAllPanelsBut("PBPropertiesPane");
				$('#messagePane').after(data);
				populatePropertiesPane($("#" + selection.Cell));
			});
		}
		
		properties.panel = 'prop';
	});
	
	// Bind the search link
	$('#cell_findSymbolsLink').bind('click', function(){
		if(properties.panel == "search") return;
		
		// Check to see if the user has performed a search already.
		if((typeof properties.searchElement) != "undefined"){
			hideAllPanelsBut("PBsearchResults");
			// User performed a search not long ago. Attach to DOM.
			$('#messagePane').after(properties.searchElement);
			$(".searchResult").draggable({ revert: true });
		} else {
			// Get the template from the server.
			getFragment("search", function(data){
				hideAllPanelsBut("PBsearchResults");
				$('#messagePane').after(data);
			});
		}
		properties.panel = 'search';
	});
	
	// Bind the erase link
	$('#cell_clearCellLink').bind('click', function(){
		if(selection.Cell != false){
			// Ask the user if they are sure.
			showMessagePane(PicBoard.languageMap.lang_253_eraseTool, null, { 
				yesnoButtons:true, 
				yesAction: function(){
					$("#" + selection.Cell).children().remove();
					$("#" + selection.Cell).parent().removeAttr('style');
					
					$('#' + selection.Cell).siblings('.ctext, .ctbottom').children('.textHolder').html("");
					//attachResize(); - shouldn't need this anymore with textHolder.

					// Reset the cell object to default.
					$.extend($("#" + selection.Cell).parent().data('cellObject'), PicBoard.cellObj);
					$("#messagePane").slideToggle();
					
					state.unsaved = true;
				 },
				 noAction: function(){
				 	$("#messagePane").slideToggle();
				 }
			});
		}
	});
	
	// Bind the duplicate link
	$('#cell_duplicateCellLink').bind('click', function(){

		if($(this).children('div').hasClass('selectedOption') == false){

			$(this).children('div').addClass('selectedOption');
		
			$('.cell:lt(' + properties.activeCells + ')').addClass('cellMovable').draggable({
				revert: true,
				helper: 'clone',
				opacity: 0.7
			});
			$( getVisibleCells() ).droppable({
				accept: ".cell",
				activeClass: 'cellHover',
				drop: function(event, ui) {
					oldGridRef = $(this).data('cellObject').gridRef;
					oldId = $(this).attr('id');

					newCell = ui.draggable.clone(true);
					newCell.attr('id', oldId);
					newCell.find('.cimg').attr('id', oldId + "img");
					newCell.find('.ctext, .ctbottom').attr('id', oldId + "text");
					
					$(newCell).data('cellObject').gridRef = oldGridRef;

					$(this).replaceWith(newCell);	
					
					if($(newCell).find('embed').length > 0){
						bindEmbedLoad();
					}				
					
					state.unsaved = true;
				}
			});
			
			// handler for deselecting the duplication tool.
			var deselectDuplicate = function(){
				$("a#cell_duplicateCellLink > div").removeClass('selectedOption');
				try { 
					$('.cell').removeClass('cellMovable').draggable('destroy');
				} catch (e){}
				try { 
					$('.cell').removeClass('cellMovable').droppable('destroy');
				} catch (e){}
				setDroppable();
				$(".ccboButtons").unbind('click', deselectDuplicate);
			}
			
			$(".ccboButtons").bind('click', deselectDuplicate);
		}
	});
	
	// Bind the deselect link
	$('#cell_deselectCellLink').bind('click', function(){
		selection.Cell = false;
		$(".cell").removeClass('cellHover');
	});
	
	// Bind the alter grid size link.
	$('#cell_gridCellsLink').bind('click', function(){
		if(state.overlayMode) {
			PicBoard.getTranslations(['lang_352_overlayNoResize'], function(data){
				alert(data.payload.lang_352_overlayNoResize);
			});
			return;
		}
		if(properties.panel == "changeGrid") return;
		
		// Check to see if the user has performed a search already.
		if((typeof properties.changeGridElement) != "undefined"){
			hideAllPanelsBut("PBresizerHolder");
			// User performed a search not long ago. Attach to DOM.
			$('#messagePane').after(properties.changeGridElement);
			
		} else {
			// Get the template from the server.
			getFragment("resizeGrid", function(data){
				// Hide all other panels
				hideAllPanelsBut("PBresizerHolder");
				
				// Insert the pane after messagePane
				$('#messagePane').after(data);
				$('#PBChartResizer').resizable({
					grid: 30,
					maxWidth:240,
					maxHeight:240,
					minWidth:30,
					minHeight:30,
					resize: function(event, ui) {
						// On resize move the tip with us
						moveResizeTip(this, $('#resizerFollower'));
						
						var newColumns = Math.floor($(this).width() / 30);
						var newRows = Math.floor($(this).height() / 30);
						
						$('#resizeTitleContainer').children("span").html(" (" + newColumns + " x " + newRows + ")");
					}
				});

				var cellWidth = $('#cell1').outerWidth();
				var parentWidth = $('#cell1').parent().width();
				var cellHeight = $('#cell1').outerHeight();
				var parentHeight = $('#cell1').parent().height();
				
				var columns = Math.floor(parentWidth / cellWidth);
				var rows = Math.floor(parentHeight / cellHeight);
				var gSize = $('#PBChartResizer').resizable('option', 'grid');

				$('#resizeTitleContainer').append($("<span>", { html: " (" + columns + " x " + rows + ")" }));

				$('#PBChartResizer').width(gSize * columns).height(gSize * rows);
				
				// Connect the resizer
				moveResizeTip($('#PBChartResizer'), $('#resizerFollower'));
				
				// Bind the apply button to alter the grid.
				$('#PBResizeApply').bind('click', function(){
					// Get old values
					
					var cellWidth = $('#cell1').outerWidth();
					var cellInternalWidth = $('#cell1').width();
					var parentWidth = $('#cell1').parent().width();
					var cellHeight = $('#cell1').outerHeight();
					var parentHeight = $('#cell1').parent().height();
					
					var columns = Math.floor(parentWidth / cellWidth);
					var rows = Math.floor(parentHeight / cellHeight);
				
					// Get new values
					var newColumns = Math.floor($('#PBChartResizer').width() / 30);
					var newRows = Math.floor($('#PBChartResizer').height() / 30);				

					// Is there a change?
					if(rows != newRows || columns != newColumns){

						$('#cell1').parent().animate({
							width: [newColumns * (cellWidth + getCellMargin("x") ), 'swing'],
							height: [newRows * (cellHeight + getCellMargin("y") ), 'swing']
						}, function() {
    						if(viewerOnly == false) detectOversizeWorkingArea();
  						});

						// Copy the cell object into each of the cells.
						var cellLoc = 1;
						var rowLength = parseInt( ($('#cell1').width() / $('#chartHolder').width()) * getCellMargin("x") );
						
						rows = Math.floor( $("#cellHolder").innerWidth() / $('#cell1').width() );
						columns = Math.floor( $("#cellHolder").innerHeight() / $('#cell1').height() );
						
						
						properties.activeCells = newColumns * newRows;
						state.unsaved = true;
						
						$('.cell').each(function(i){
							
							// Work out what cell we are - x,y.
							x = Math.ceil(cellLoc / rows);
							y = Math.ceil(cellLoc - x*rows + rows);
							
							$(this).data('cellObject').gridRef = [y, x];
							
							cellLoc++;
							
						});
		
						// Set the status in the ChartObject.
						$('#chartHolder').data('chartObject').properties.rows = newRows;
						$('#chartHolder').data('chartObject').properties.columns = newColumns;
						
						if(viewerOnly == false) attachResize();
					}
					
				});
			});
		}
		properties.panel = 'changeGrid';
	});
}

function moveResizeTip(leader, follower){
	// Add the tip image.
	// Get the resizer position.
	resizer = $(leader);
	currentPos = resizer.offset();
	
	// Calculate placement
	tipTop = currentPos.top + parseInt(resizer.height());
	tipLeft = currentPos.left + parseInt(resizer.width());
	
	// Set placement
	$(follower).css({ top: tipTop, left: tipLeft });
}

function ieOverflowFixer(){
	// Check to see if the working area is zoomable.
	if( parseInt($('#cellHolder').width()) > parseInt($('#chartHolder').width()) || parseInt($('#cellHolder').height()) > parseInt($('#chartHolder').height()) ) {
		if($.browser.msie == true) {
			$('#chartHolder').css({'overflow': 'scroll' });
			$('#chart').css('overflow', 'hidden');
		}
	} else {
		if($.browser.msie == true) {
			$('#chartHolder').css({'overflow': 'scroll', 'width': parseInt($('#chartHolder').width())+10 + "px" });
			$('#chart').css('overflow', 'hidden');
		}
	}
}

function detectOversizeWorkingArea(){
	// Reset working area back to normal
	zoomInWorkingArea();

	// Check to see if the working area is zoomable.
	if( parseInt($('#cellHolder').width()) > parseInt($('#chartHolder').width()) || parseInt($('#cellHolder').height()) > parseInt($('#chartHolder').height()) ) {
		setWorkingAreaZoomable();
	} else {
		// Remove the zoom controls (if they are present)
		setWorkingAreaNormal();
	}
	
	if($.browser.msie == true) ieOverflowFixer();
}

function setWorkingAreaZoomable(){
	// The working area is zoomable. Add controls for zooming in.
	
	if($('#zoomController').length == 0){
	
		container = $('<div>', { id: 'zoomController' });
		
		$('<a>', { href: '#', 'class':'zoomLink', click: function(){ zoomOutWorkingArea() } }).append( $('<img>', { src: '../images/magnifier-zoom-out.png' }), " " + PicBoard.languageMap.lang_267_zoomOut ).appendTo(container);
		
		$('#PBchartNameParentHolder').append(container);
	
	}
}

function setWorkingAreaNormal(){
	// Remove the zoom controls
	if($('#zoomController').length > 0){
		// Controller exists. Remove.
		$('#zoomController').remove();
	}
}

function zoomInWorkingArea(){
	// Set the cell size back to 200.
	cellSize = 200;
	
	// Get the number of rows and columns we're dealing with
	rows 	= $('#chartHolder').data('chartObject').properties.rows;
	columns = $('#chartHolder').data('chartObject').properties.columns;
	
	comittedSize = setCellsBox(cellSize);
	
	// Change the canvas size.
	setCanvasSize( 
		parseInt( (comittedSize.width  + getCellOuterOffsets("x") ) * columns),
		parseInt( (comittedSize.height + getCellOuterOffsets("y") ) * rows) 
	);
	
	// Change button to zoom in.
	link = $('.zoomLink');
	
	// Unbind the event.
	link.unbind('click');
	// Bind the zoom in button
	link.bind('click', function(e){ 
		zoomOutWorkingArea();
		e.bubbles = false;
	});
	// Change the text.
	link.html("");
	link.append( $('<img>', { src: '../images/magnifier-zoom-out.png' }), " " + PicBoard.languageMap.lang_267_zoomOut );
}

function zoomOutWorkingArea(){

	// Make the working area fit the canvas size.
	// What is the canvas size?
	canvasSize = parseInt($('#chartHolder').width());
	
	// Get the cell size.
	cellSize = getCellDimensions();
	
	// Get the number of rows and columns we're dealing with
	rows 	= $('#chartHolder').data('chartObject').properties.rows;
	columns = $('#chartHolder').data('chartObject').properties.columns;
	
	// We need to work out a size to make the cells, so that they will fit on-screen.
	// We also need to take into account X and Y, to preserve dimensions of grid.

	diffx = parseInt($('#cellHolder').width()) - parseInt($('#chartHolder').width());
	diffy = parseInt($('#cellHolder').height()) - (parseInt($('#chartHolder').height()) - getChartDescriptorHeight());
	
	
	if(diffx > 0 || diffy > 0){
	
		if(diffy >= diffx){
			resizeCellsByY(diffy, columns, rows);
		} else {
			resizeCellsByX(diffx, columns, rows);
		}
		
		
		// Change button to zoom out.
		link = $('.zoomLink');
		
		// Unbind the event.
		link.unbind('click');
		// Bind the zoom in button
		link.bind('click', function(e){ 
			zoomInWorkingArea();
			e.bubbles = false;
		});
		// Change the text.
		link.html( "" );
		link.append( $('<img>', { src: '../images/magnifier-zoom-in.png' }), " " + PicBoard.languageMap.lang_266_zoomIn );

	}
	
}

function resizeCellsByX(diff, columns, rows){

	// Get the cell size.
	cellSize = getCellDimensions();
	// We're dealing with more x than y. Size appropriatley.
	
	// How much do we need to shrink the cells by to make them fit?
	toSubtract = parseInt(diff / columns ) + 2;
	
	//currentSize - (diff / rows)
	
	// Get the new size of the cell.
	newSize = cellSize.width - toSubtract;
	
	// Commit the cell dimensions to the cells on-canvas.
	comittedSize = setCellsBox(newSize);
	
	// Change the canvas size.
	setCanvasSize( parseInt($('#chartHolder').width()), ( (comittedSize.height  + getCellOuterOffsets("y") ) * rows) + 10);
}

function resizeCellsByY(diff, columns, rows){
	// We're dealing with more y than x. Size appropriatley.
	
	// How much do we need to shrink the cells by to make them fit?
	toSubtract = ( diff / rows )+2;
	
	// Get the new size of the cell.
	newSize = cellSize.height - toSubtract;
	
	// Commit the cell dimensions to the cells on-canvas.
	comittedSize = setCellsBox(newSize);
	
	// Change the canvas size.
	setCanvasSize( ( (comittedSize.width + getCellOuterOffsets("x") ) * columns) + 10, parseInt( $('#chartHolder').height() - (getChartDescriptorHeight() + 5) ));
}

function getCellMargin(axis){
	if(axis == "y"){
		return parseInt($('#cell1').css('margin-top')) + parseInt($('#cell1').css('margin-bottom'));	
	}
	
	if(axis == "x"){
		return parseInt($('#cell1').css('margin-left')) + parseInt($('#cell1').css('margin-right'));
	}
}

function getMargin(target, axis){
	if(axis == "y"){
		return parseInt($(target).css('padding-top')) + parseInt($(target).css('padding-bottom'));	
	}
	
	if(axis == "x"){
		return parseInt($(target).css('padding-left')) + parseInt($(target).css('padding-right'));
	}
}

function getCellBorder(axis){
	return getBorder("#cell1", axis);
}

function getBorder(pointer, axis){
	if(axis == "x"){
		return parseInt($(pointer).css('border-left-width')) + parseInt($(pointer).css('border-right-width'));
	}
	
	if(axis == "y"){
		return parseInt($(pointer).css('border-top-width')) + parseInt($(pointer).css('border-bottom-width'));	
	}
}

function getCellOuterOffsets(axis){
	if(axis == "x"){
		return parseInt($('#cell1').css('margin-left')) + parseInt($('#cell1').css('margin-right')) + parseInt($('#cell1').css('border-left-width')) + parseInt($('#cell1').css('border-right-width'));
	}
	
	if(axis == "y"){
		return parseInt($('#cell1').css('margin-top')) + parseInt($('#cell1').css('margin-bottom')) + parseInt($('#cell1').css('border-top-width')) + parseInt($('#cell1').css('border-bottom-width'));	
	}
}

function getCellOffsets(cell, axis){
	if(axis == "x"){
		return parseInt($(cell).css('margin-left')) + parseInt($(cell).css('margin-right')) + parseInt($(cell).css('border-left-width')) + parseInt($(cell).css('border-right-width'));
	}
	
	if(axis == "y"){
		return parseInt($('#cell1').css('margin-top')) + parseInt($('#cell1').css('margin-bottom')) + parseInt($('#cell1').css('border-top-width')) + parseInt($('#cell1').css('border-bottom-width'));	
	}
}

function getCellDimensions(){
	return { height: parseInt($('#cell1').height()), width: parseInt($('#cell1').width()) };
}

function getChartDescriptorHeight(){
	holder = $('#PBchartNameParentHolder');
	height = 0;
	
	height += holder.height();
	height += parseInt(holder.css('margin-top')) + parseInt(holder.css('margin-bottom'));
	height += parseInt(holder.css('padding-top')) + parseInt(holder.css('padding-bottom'));
	
	return height;
}

function setCanvasSize(x, y){
	$('#cellHolder').css({ height: parseInt(y), width: parseInt(x) });
}

function setCellMargin(margin){
	val = parseInt(margin);
	
	$('.cell').each(function(i){ $(this).css({ margin:val+'px', width: ($(this).width()-val*2), height: ($(this).height() - val*2) }) });

	$('.ctext, .ctbottom').each(function(i){ $(this).css({ width: ($(this).width()-val*2) }) });
}

function setCellsBox(box){
	box = parseInt(box);
	
	setCellDimensions('.cell', box, box);
	return { height: box, width: box };
}

function setCellDimensions(identifier, height, width){
	height = parseInt(height);
	width = parseInt(width);
	$(identifier).each(function(i){ 
		// Set the width and height
		$(this).css({ width: width, height: height }) 
		
		// Are there any images?
		image = $(this).find("img, embed");
		if(image.length > 0){
			resizeImage(image, $(this));
		}
	});
	
	// Resize content holders
	$('.ctext, .ctbottom').each(function(i){ $(this).css({ width: width }) });
	$('.cimg').each(function(i){ $(this).height(height - (parseInt($(this).siblings('.ctext, .ctbottom').height() + getCellMargin("y")) )) });
	
	return { height: height, width: width };
}

function cloneCellAndInsertBlank(from, insertAfter, newID){
	// Clone the element.
	var newEl = $(from).clone();
	var currentID = $(newEl).attr('id');
	
	// Replace the ID.
	$(newEl).attr('id', 'cell' + newID).removeClass('cellHover');
	// Replace child id's
	$(newEl).children("#" + currentID + "text").attr('id', 'cell' + newID + 'text');
	$(newEl).children("#" + currentID + "img").attr('id', 'cell' + newID + 'img');
	
	// Insert the new element
	$(insertAfter).after( $(newEl) );
	
	// Extend the data object.
	$(newEl).data('cellObject', jQuery.extend({}, PicBoard.cellObj) );
	
	//console.dir($(newEl).data('cellObject'));
	populateCellFromData("#" + $(newEl).children('.cimg').attr('id'));
}

function diffObject(src, dest){
	var diff = 0;
	diff += (src.PBCBold == dest.PBCBold) ? 0 : 1;
	diff += (src.PBCitalic == dest.PBCitalic) ? 0 : 1;
	diff += (src.PBCunderline == dest.PBCunderline) ? 0 : 1;
	diff += (src.bgcolour == dest.bgcolour) ? 0 : 1;
	diff += (src.colour == dest.colour) ? 0 : 1;
	diff += (src.fontFace == dest.fontFace) ? 0 : 1;
	diff += (src.frameColour == dest.frameColour) ? 0 : 1;
	diff += (src.image == dest.image) ? 0 : 1;
	diff += (src.text == dest.text) ? 0 : 1;
	diff += (src.textAlignment == dest.textAlignment) ? 0 : 1;
	diff += (src.textHeight == dest.textHeight) ? 0 : 1;
	diff += (src.textLocation == dest.textLocation) ? 0 : 1;
	diff += (src.textSize == dest.textSize) ? 0 : 1;
	diff += (src.imageID == dest.imageID) ? 0 : 1;
	diff += (src.source == dest.source) ? 0 : 1;
	
	return diff;
}

function populateChartFromData(){
	//Chart name
	var data = $('#chartHolder').data('chartObject');
	$('#PBchartNameHolder').text(data.title);
}

function populateCellFromData(cellID){
	// Load the data from the Cell Object and apply it to the cell.
	var dataObject = $(cellID).parent().data("cellObject");
	
	if(dataObject == null) return;
	
	// Bold, italic, underline
	(dataObject.PBCbold) ? $(cellID).parent().css('font-weight', 'bold') : $(cellID).parent().css('font-weight', 'normal');
	(dataObject.PBCitalic) ? $(cellID).parent().css('font-style', 'italic') : $(cellID).parent().css('font-style', 'normal');
	(dataObject.PBCunderline) ? $(cellID).parent().css('text-decoration', 'underline') : $(cellID).parent().css('text-decoration', 'none');
	
	// Text Alignment
	$(cellID).siblings('.ctext, .ctbottom').css('text-align', dataObject.textAlignment );
	
	// Text Location
	if( dataObject.textLocation == "bottom" ){
		$(cellID).prev().insertAfter(cellID);
	
		$(cellID).siblings('.ctext').addClass('ctbottom').removeClass('ctext');
	} else {
		$(cellID).insertAfter($(cellID).next());
		$(cellID).siblings('.ctbottom').addClass('ctext').removeClass('ctbottom');		
	}
	
	if(viewerOnly == false) attachResize();
	
	$(cellID).siblings(".ctext, .ctbottom").children('.textHolder').html(dataObject.text);
	
	$(cellID).parent().css({
		'color': dataObject.colour, 'background-color': dataObject.bgcolour, 'border-color': dataObject.frameColour, // Colours
		'font-family': dataObject.fontFace, // Font
		'font-size': dataObject.textSize // Text Size
	});
	
	// Image
	if(dataObject.image != ''){
		$(cellID).html( $( browserDependentTag(dataObject.image)).attr('src', browserDependentImage(dataObject.image) ) );
	} else {
		$(cellID).children('img').remove();
	}
	
	// Text Height
	$(cellID).siblings('.ctext, .ctbottom').height(dataObject.textHeight + "px");
	$(cellID).css("height", parseInt($(cellID).parent().height() - dataObject.textHeight) + "px");

	// Resize images.
	var f_image = $(cellID).find('embed, img');

	if(f_image.length == 1) resizeImage( f_image, $(cellID) );
	resizeCimg( $( cellID ).parent() );
}

function hideAllPanelsBut(exc){
	// Hide all side panels, except the specified one
	$('#CellcontrolHolder').siblings("[id!=messagePane]").each(function(i, e){
		var id = $(this).attr('id');
		if(id == "PBsearchBox") properties.searchElement = $('#PBsearchBox').detach();
		if(id == "PBPropertiesPane") properties.propertiesElement = $("#PBPropertiesPane").detach();
	});

	$('#CellcontrolHolder').siblings("[id!=messagePane]").remove();
}

function setupChartLocking(){
	// Locking / unlocking of cells
	$('#unlock_chart').bind('click', function(){
		// Make the items in the chart 'sortable' (i.e. you can move them around the chart)
		if(state.chartDraggable == false){
			$("#cellHolder").sortable({
				// Called when user drops a cell.
				stop: function(event, ui){
					
					cellLoc = $(ui.item).prevAll().length + 1;
					
					var rowLength = parseInt( ($('#cell1').width() / $('#chartHolder').width()) * getCellMargin("x") );
					
					y = Math.ceil(cellLoc / rowLength);
					x = Math.ceil(cellLoc - y*rowLength + rowLength);
					
					$(ui.item).data('cellObject').gridRef = [x, y];
				}
			});
			state.chartDraggable = true;
			$('#unlock_chart div span').addClass('ui-lockunlock');
		} else {
			$("#cellHolder").sortable('destroy');
			state.chartDraggable = false;
			$('#unlock_chart div span').removeClass('ui-lockunlock');
		}
	});
}

function performSearch(library, term, type, page){

	if(term != "" && term.length >= 3){
	
		// Clear cell selection
		selection.Cell = false;
		$(".cell").removeClass('cellHover');
	
		// Change the UI to reflect searching status.
		$('#remoteResults').html($("<img/>", { src : PicBoard.baseURL + "/images/loading.gif" }));
		$('#resultStatus').html(PicBoard.languageMap.lang_251_searching);

		// Insert the photos area if its not already there. If it is, blank it.
		if($('#otherPhotosHolder').length > 0){
			$('#personalHolder').html("");
		} else {
			$('#resultStatus').after(
				$('<div>', { id: 'otherPhotosHolder' }).append(
					$('<h1>', { text: "Photos and other images" }),
					$('<div>', { id:'personalHolder' })
				)
			);
		}
	
		var sendData = { "term" : term, "lib" : library, "l" : PicBoard.language, "type" : type, "page": page};
	
		$.ajax({
			type: "POST",
			url: PicBoard.baseURL + "rsrc.php?req=performSearch",
			data: sendData,
			dataType: 'json',
			success: function(data){
				//data.itemCount = total displayed results
				//data.totalItemCount = total items
				//data.page
				//data.pageCount = total number of pages
				//data.symbols = array of symbols
				$('#resultStatus').html(data.totalItemCount + " symbols found. Page " + (data.page + 1) + " of " + data.pageCount + ".");
				
				// Set the current search page.
				state.queryPage = page;
				state.queryPages = data.pageCount;
				state.queryItems = data.totalItemCount;
				
				
				// Is there more than one page?
				if( data.pageCount > 1 ){
					
					nextButton = $('<button>', { 
						text: PicBoard.languageMap.lang_223_nextPage,
						click: function(){	
							// Check to see if we can go back / forward...
							if( (state.queryPage + 1) < state.queryPages ){
								performSearch('StraightStreet', $("#PBsearchControl").val(), 'symbols', state.queryPage + 1);
							}
						}
					});
					
					prevButton = $('<button>', { 
						text: PicBoard.languageMap.lang_185_previousPage,
						click: function(){
							// Check to see if we can go back / forward...
							if( state.queryPage > 0 ){
								performSearch('StraightStreet', $("#PBsearchControl").val(), 'symbols', state.queryPage - 1);
							}
						}
					});
					
					
					$('#resultStatus').append($("<br />"), prevButton, nextButton);
					
					// IE sucks
					if($.browser.msie != true) $('button').button();
				}
				
				// Display the results we've received
				displayResults(data.symbols, term);
				

				// If we have personal images display
				if(data.personalImages.length > 0){
					displayPersonalImages(data.personalImages, term);
				} else {
					$('#personalHolder').html(PicBoard.languageMap.lang_248_noResults);
				}
			}
		});
		
		delete sendData;
	}
}

function displayResults(results, term){
	$('#remoteResults').empty();
	
	if(results.length > 0){
		// If we have more than one search result.	
		i = 1;
		for(var o in results){
			// Replace underscores with spaces
			results[o].name = results[o].name.replace(/_/g, " ");
			displayName = results[o].name.length > 7 ? results[o].name.substring(0, 7) + "..." : results[o].name;
			
			$('#remoteResults').append($('<div/>', { 'class': 'searchItemContainer'})
			 	.html('<p>' + displayName + '</p>')
			 	.append($('<div/>', { id: 'res' + i, 'class': 'searchResult', data: results[o] })
			 	.append($('<img/>', { src: results[o].thumbnailURL, title: results[o].name, alt: results[o].name }))
			));
			
			i++;
		}
		$(".searchResult").draggable({ start: function(event, ui) {
				if($.browser.msie == false) {
					// Preload SVG's					
					var m = $(this).data("imageSVGURL").match(/http:\/\/(.*?)$/);
			
					var cache = new Image();
					cache.src = PicBoard.baseURL + "SVGProxy.php?r=" + m[1];
					//console.log($(this).data());
				}
			},
			revert: true
		});

		
		// Tip for extended names
		$('.searchResult > img').tipsy({gravity: 's'});
		
		// Disable selection in the search results area.
		$('#PBsearchResults').disableSelection();
		
		attachSelectables();
	} else {
		$('#remoteResults').html(PicBoard.languageMap.lang_248_noResults);
	}
}

function displayPersonalImages(results, term){
	//personalImages
	
	supportedImages = removeUnsupportedImages(results);
	
	removedImages = 0;
	
	i = 1;
	for(var o in supportedImages){
		// Check to see if this browser supports this image type.

	
		// Replace underscores with spaces
		supportedImages[o].name = supportedImages[o].name.replace(/_/g, " ");
		displayName = supportedImages[o].name.length > 7 ? supportedImages[o].name.substring(0, 7) + "..." : supportedImages[o].name;
		
		$('#personalHolder').append($('<div/>', { 'class': 'searchItemContainer'})
		 	.html('<p>' + displayName + '</p>')
		 	.append($('<div/>', { id: 'pres' + i, 'class': 'searchResult personalPhotoContainer', data: supportedImages[o] })
		 	.append($('<img/>', { src: supportedImages[o].imageURL, title: supportedImages[o].name, alt: supportedImages[o].name, style: 'display:hidden' }))
		));
		
		
		container = $('#' + 'pres' + i);
		
		callback = function(){
			var newImage = container.find('img');
			
			newImage.wrap($("<div>", { 'class': 'pImgWrapper' }))
			
			/*
			// Try and center the image.
			var hWidth = "" + (newImage.width() / 2) + "px";
			var hHeight = "" + (newImage.height() / 2) + "px";
			console.log(hWidth + " " + hHeight);
			$(newImage).addClass("js-fix").css({
				"margin-left" : hWidth,
				"margin-top" : hHeight
			});
			*/
		}
		
		resizeImage(container.find('img'), container, callback);
		
		i++;
	}
	
	if(results.length > supportedImages.length){
		// Some images were removed.
		invalidImageCount =  " (" + (results.length - supportedImages.length) + " invalid)";
	} else {
		invalidImageCount = "";
	}
	
	$('#imageResultStatus').remove();
	
	$('#personalHolder').after(
		$('<div>', { id: 'imageResultStatus' }).text(supportedImages.length + ' images found' + invalidImageCount + '.')
	);
	
	$('#personalHolder').find(".searchResult").draggable({ 
		start: function(event, ui) {
			if($.browser.msie == false) {
				// Get the filetype.
				var src = $(this).find('img').attr('src');
				extension = src.match(/([^\.]+)$/);
				
				if(extension[1] == "svg"){
					// Preload SVG's
					//var cache = new Image();
					//cache.src = src;
				}
			}
		},
		revert: true
	});
}

function removeUnsupportedImages(images){
	// Return array of images without unsupported ones.
	var allowedImages = [];
	// Detect the browser
	
	// Are we webkit / FireFox / Opera?
	// No WMF's
	if($.browser.webkit || $.browser.mozilla || $.browser.opera){
		for(var o in images){
			extension = images[o].imageURL.match(/([^\.]+)$/);
			if(extension[1].toLowerCase() != "wmf") allowedImages.push(images[o]);
		}
	}
	
	// Are we IE?
	// No SVG's
	if($.browser.msie){
		for(var o in images){
			extension = images[o].imageURL.match(/([^\.]+)$/);
			if(extension[1].toLowerCase() != "svg") allowedImages.push(images[o]);
		}
	}

	return allowedImages;
}

function setupSearching(){
	$("#PBsearchControl").keydown(function(e){
		if(e.keyCode == 13){
			performSearch('StraightStreet', $("#PBsearchControl").val(), 'symbols', 0);
		}
	});
	
	$('#PBsearchButton').bind('click', function(){
		performSearch('StraightStreet', $("#PBsearchControl").val(), 'symbols', 0);
	});
	
}

function getAllVisibleCellData(){
	var returnData = Array();
	$('.cell:lt(' + properties.activeCells + ')').each(function(index){
		returnData.push( $(this).data('cellObject') );
	});
	return returnData;
}

function getChartAttributes(){
	// Work out the cells that are on screen.
	if(state.overlayMode){
		var columns = $('#chartHolder').data('overlayObject').columns;
		var rows = $('#chartHolder').data('overlayObject').rows;
		var cells = $('#chartHolder').data('overlayObject').cellCount;
	} else {
		var columns = Math.floor( $("#cellHolder").innerWidth() / $('#cell1').width() );
		var rows = Math.floor( $("#cellHolder").innerHeight() / $('#cell1').height() );
		var cells = columns * rows;
	}
	return { 'columns': columns, 'rows': rows, 'cells':cells };
}

function checkForUnsavedWork(){
	if(state.unsaved){
		return PicBoard.languageMap.lang_282_unsavedWork;
	} else {
		return;
	}
	
}

function getAvailableBooks(target){
	// Clear the current selection

	$(target).children(':gt(1)').remove();

	$.get(PicBoard.baseURL + 'rsrc.php?req=getUserBooks', function(data){
		
		$.each(data.books, function(index, value){
			$(target).append( $("<option>", { 'val': parseInt(value.bID), 'text': value.name }) );
		});
	}, 'json');
	
}

function getAvailableGroupBooks(target, gID){
	// Clear the current selection
	
	$(target).children(':gt(1)').remove();
	
	$.post(PicBoard.baseURL + 'rsrc.php?req=getGroupBooks', { 'gID': gID }, function(data){
		if(data.books.length > 0){
			$.each(data.books, function(index, value){
				$(target).append( $("<option>", { 'val': parseInt(value.bID), 'text': value.name }) );
			});
		}
	}, 'json');
}

function getPrivateImages(target){
	
	target = $(target);
	// Pull out all of the images in the chart that are private.
	
	// Get the cell data
	cellData = getAllVisibleCellData();
	
	// Build up a list of images that are private.
	privateImages = [];
	
	$.each(cellData, function(index, data){
		if(data.image == "albumimage" || data.image.match(/imagestore/i)){
			// This is a cell with images in.
			privateImages.push({ cellID: index, 'image': data.image });
		}
	});
	
	$('#privimageContainer').remove();
	state.disabledImages = [];
	
	
	container = $('<div>', { id: 'privimageContainer' });
	
	$.each(privateImages, function(i, data){
		container.append(
			$('<a>', { 'class': 'ui-corner-all loadItem userItem userList imageParent removableImage' }).bind('click', function(){
					state.disabledImages.push( $(this).children('img').data('cellID') );
					
					$(this).remove();
					
					if($("#privimageContainer").children().length == 0){
						$('.imageClearer, #privimageContainer').remove();
					}
				}).append(
				$('<img>', { src: data.image, height: '50px', width: '50px' }).data('cellID', data.cellID)
			)
		);
	});
	
	target.after(container, $('<br>', { 'class':'cleft imageClearer' }));
}

function setupToolbar(){
	$("#save_chart").bind('click', function(){ 
		if($('#chartHolder').data('chartObject').title == "") {
			alert(PicBoard.languageMap.lang_250_chartUnique);
		} else {
			if($('#chartHolder').data('chartObject').chartID == 0){
				saveDialog();
			} else {
				saveChart();
			}
		}
	});
	
	$('#save_as_chart').bind('click', function(){
		saveAsDialog();
	});
	
	$('#print_chart').bind('click', function(){
		tryPrint();
	});
	
	$('#delete_chart').bind('click', function(){
		deleteChart();
	});
	
}

function saveAsDialog(){
	getFragment("saveAsDialog", function(data) {
		jQuery.facebox(data);
		
		// Save the default state for the save button somewhere
		state.defaultSaveText = $('button#confirm_save_chart').text();
		
		// Load the chart name
		$('#chart_name').val( $('#chartHolder').data('chartObject').title );
		
		// Is there any material that may cause problems (i.e. private images?)
		privateChart = checkChartPrivacyOnSave();
		
		// Get available books from the user's library
		getAvailableBooks("select#savein_book");
		
		// If the user selects a location that is a group
		$("select#save_location").unbind('change');
		$("select#save_location").bind('change', function(){
			
			// If the selected value is not 0 then this is a group.
			if( parseInt($(this).val()) != 0 && privateChart){
				// The user may have come from another group, so remove the donateExplain container
				$('#donateExplain').remove();
				
				$('select#save_location').after(
					$('<p>', { 'class': 'ui-state-highlight ui-corner-all smText' , 'id': 'donateExplain'})
						.html(PicBoard.languageMap.lang_285_donationConfirmation)
						.wrap($('<div>', { 'class': 'ui-widget' })).slideToggle()
				);
				
				// Get the private images in this chart and append after donateExplain
				getPrivateImages("#donateExplain");
				
				// Get available group books that we can save in.
				getAvailableGroupBooks("select#savein_book", parseInt($(this).val()));
				
				$('button#confirm_save_chart').children('span').text($('input#donateString').val());

			} else {
				// Selected item is not a group, so remove anything associated with groups and reset back to default layout
				$('#donateExplain').remove();
				$('#privimageContainer').remove();
				$('button#confirm_save_chart').children('span').text(state.defaultSaveText);
			}
		});
		
		$('button#confirm_save_chart').bind('click', function(){
			// Bind the send button.
			saveAndDuplicateChart();
		});
	});
}

function tryPrint(){
		//page = document.location.toString().match(/\/(\w+)\?.*?$/);
		
		var qs = window.location.search;
		// Get the id from the request URL
		var id = qs.match(/\?.*?id=(\w+)/);
		
		if(id == null || id == 0){
			var id = ['',  $('#chartHolder').data('chartObject').chartID];
		}
		
		if(id != null && id[1] != 0) {
			if(typeof showPrintDialog != "undefined"){
				showPrintDialog(id[1]);
			} else {
				window.location = "print?id=" + id[1];
			}
		} else {
			PicBoard.getTranslations(['lang_273_saveChartfirst'], function(data){
				alert(data.payload.lang_273_saveChartfirst);
				saveDialog( function(){ tryPrint(); } );
			});
		}
}

function checkChartPrivacyOnSave(){
	disabledChart = false;
	
	cells = getAllVisibleCellData();
	
	$.each(cells, function(index, data){
		if(data.image == "albumimage"){
			disabledChart = true;
		}
	});
	
	if(disabledChart == false){
		for(group in PicBoard.managedGroups){
			$("select#save_location").append( $("<option>", { 'val': PicBoard.managedGroups[group][0], 'text': PicBoard.managedGroups[group][1] }) );
			
			$("select#save_location").bind('change', function(){
				getAvailableGroupBooks("select#savein_book", parseInt($(this).val()));
			});
		}
	} else {
		// If the chart has personal images in & there are available groups.
		if(PicBoard.managedGroups.length > 0){
			$('select#save_location').after(
				$('<p>', { 'class': 'ui-state-highlight ui-corner-all' })
					.html(PicBoard.languageMap.lang_274_noGroupOption)
					.wrap($('<div>', { 'class': 'ui-widget' }))
			);
		}
	}
	
	return disabledChart;
}

function saveDialog(callback){
	// If the chart has any images in that are from the users library, they cannot save these to a group.
	
	getFragment("saveDialog", function(data) {
		jQuery.facebox(data);
		
		checkChartPrivacyOnSave();
		
		getAvailableBooks("select#savein_book");
				
		// Map the save button.
		$('button#confirm_save_chart').bind('click', function(){
			// Close the dialog
			jQuery.facebox.close();
			// Commit the data
			$('#chartHolder').data('chartObject').groupID = $("select#save_location").val();
			$('#chartHolder').data('chartObject').bookID = $("select#savein_book").val();
			$('#chartHolder').data('chartObject').visibility = $('input[type=radio][name=chart_private]:checked').val();
			// Invoke save
			saveChart(callback);
		});
	});
}

function saveChart(callback){
	
	var alternateNotification = false;
	
	// Is the save pane already visible?
	if($('#messagePane').css('display') == "none"){
		showMessagePane(PicBoard.languageMap.lang_254_savingChart, null, null);
	} else {
		alternateNotification = true;
	}
	
	attributes = getChartAttributes();
	
	// Get the overlay being used (if any).
	var overlay = $('#chartHolder').data('overlayObject').identifier;
	
	// Work out the cells that are on screen.
	columns = attributes.columns;
	rows = attributes.rows;
	cells = attributes.cells;
	
	// Set out layout of data object we're going to be sending.
	var sendData = { chartSettings: { 'columns': columns, 'rows': rows, 'overlay': overlay, 'cells': cells }, cellSettings: [] };
	
	// Get the chart information.
	jQuery.extend(sendData.chartSettings, $('#chartHolder').data('chartObject').returnProperties());
	
	// Get the data for all active cells
	for(i=0;i<cells;i++){
		// Get the data object for the cell
		var properties = $('#cell' + (i+1)).data('cellObject').returnProperties();
		// Allocate it to the sendData item corresponding to this cell.
		sendData.cellSettings[i] = properties;
	}
	
	
	// OK, data collection complete.
	// Paramatise the data we're sending.
	sendData = jQuery.param(sendData);
	
	// Send the data off to the server.
	$.post(PicBoard.baseURL + 'rsrc.php?req=saveChart', sendData, function(data){
		try {
			if(data.statusCode == 200){
				
				if(alternateNotification){
					alert(PicBoard.languageMap.lang_286_chartSaved);
				} else {				
					// Hide the saving pane
					$("#messagePane").slideToggle();
				}
				
				if(typeof data.payload != "undefined"){
					// Set the chartID to the new ID in the database.
					$('#chartHolder').data('chartObject').chartID = data.payload.chartID;
				}

				state.unsaved = false;
				
				// Successful save, do we have a callback?
				if(typeof callback == "function"){
					callback();
				}
				
			} else {
				alert(data.message);
			}
		
		} catch(err){
		
		}
		
	}, 'json');
	
}

function saveAndDuplicateChart(){
	var alternateNotification = false;
	
	// Is the save pane already visible?
	if($('#messagePane').css('display') == "none"){
		showMessagePane(PicBoard.languageMap.lang_254_savingChart, null, null);
	} else {
		alternateNotification = true;
	}
	
	attributes = getChartAttributes();
	
	// Work out the cells that are on screen.
	columns 	= attributes.columns;
	rows 		= attributes.rows;
	cells 		= attributes.cells;
	
	// Get the overlay being used (if any).
	var overlay = $('#chartHolder').data('overlayObject').identifier;
		
	
	// Set out layout of data object we're going to be sending.
	var sendData = { chartSettings: { 'columns': columns, 'rows': rows, 'overlay': overlay, 'cells': cells }, cellSettings: [] };
	
	// Get the chart information.
	jQuery.extend(sendData.chartSettings, $('#chartHolder').data('chartObject').returnProperties());
	
	// Put the data that the user has entered from the submission form into the data we are sending the server
	sendData.chartSettings.groupID 		= $("select#save_location").val();
	sendData.chartSettings.bookID 		= $("select#savein_book").val();
	sendData.chartSettings.visibility 	= $('input[type=radio][name=chart_private]:checked').val();	
	sendData.chartSettings.title		= $('input#chart_name').val();
	
	// Get the data for all active cells
	for(i=0;i<cells;i++){
		// Get the data object for the cell
		var properties = $('#cell' + (i+1)).data('cellObject').returnProperties();
		// Allocate it to the sendData item corresponding to this cell.
		
		// is the save location not in our own library? If so, we need to identify the images that we are keeping / removing

		if( parseInt($("select#save_location").val()) != 0 && typeof state.disabledImages != "undefined"){
		
			if(i in ato(state.disabledImages)){
				// This image has been removed by the user. Remove it from the cell object.
				properties.image 	= "";
				properties.imageID 	= null;
			} else {
				// Is this a symbol?
				if( /straight-street/i.test(properties.image) == false){
					properties.imageCopyAuthorised = true;
				}
			}
		}
		
		sendData.cellSettings[i] = properties;
	}

	
	// OK, data collection complete.
	// Paramatise the data we're sending.
	sendData = jQuery.param(sendData);
	
	// Send the data off to the server.
	$.post(PicBoard.baseURL + 'rsrc.php?req=saveAsChart', sendData, function(data){
		try {
			if(data.statusCode == 200){
				
				if(alternateNotification){
					alert(PicBoard.languageMap.lang_286_chartSaved);
				} else {				
					// Hide the saving pane
					$("#messagePane").slideToggle();
				}

				state.unsaved = false;
				
				// All OK, commit the data
				$('#chartHolder').data('chartObject').groupID = $("select#save_location").val();
				$('#chartHolder').data('chartObject').bookID = $("select#savein_book").val();
				$('#chartHolder').data('chartObject').visibility = $('input[type=radio][name=chart_private]:checked').val();
				jQuery.facebox.close();
				
				alert(PicBoard.languageMap.lang_287_copySaved);
				
			} else {
				alert(data.message);
				$('#messagePane').hide();
			}
		
		} catch(err){
			
		}
		
	}, 'json');	

}

function deleteChart(){
	var chartID = $('#chartHolder').data('chartObject').chartID;
	
	if(chartID != ""){
		if(confirm(PicBoard.languageMap.lang_249_deleteChart)) {
			$.post(PicBoard.baseURL + "rsrc.php?req=deleteChart", {'id': chartID}, function(data){
			
				if(data.statusCode == 200){
					window.location = "home";
				} else {
					alert(data.message);
				}
			}, 'json');
		}
	}
}

// Display a handle on the top right of a cell to allow the user to drag, without having to 'enable' drag & drop.
function displayDragHandle(target){
	var d = dragDrop, t = $(target);
	if(d.dragging) return;
	
	// If we're displaying for the first time the handle won't have a height and width. Default to 20.
	var hw = d.handle.width() == 0 ? 20 : d.handle.width();
	var hh = d.handle.height() == 0 ? 20 : d.handle.height();

	var offset = t.offset();
	var holdOffset = $('#chartHolder').offset();

	// Get scroll positions
	var sl = $('#chartHolder').scrollLeft();
	var st = $('#chartHolder').scrollTop();
	
	// Calculate where to place the handle
	var left = (offset.left - holdOffset.left) + t.width() - hw + sl;
	var top = (offset.top - holdOffset.top) - hh + st;
	
	d.handle.css({'left': left, 'top': top});
	$(target).append(d.handle);
	d.handle.show();
	
	d.activated = true;
	clearTimeout(d.closeTimer);

	d.closeTimer = setTimeout(function(){
		deactivateDragHandle();
	}, 5000);
}

function deactivateDragHandle(){
	var d = dragDrop;
	if(!d.activated) return;

	d.handle.hide();
	d.activated = false;
}

function attachOverlayButton(){
	$('#cell_templates').bind('click', function(){
		// If we're already in template mode, switch the user back to normal mode.
		if(state.overlayMode){
			//switch off
			var overlayData = $('#chartHolder').data('overlayObject');
			
			overlayData.identifier = "defaultFormat";
			
			$('#chartHolder').data('overlayObject', overlayData);
			
			var callback = function(){
				if(/template=/i.test(window.location)){
					window.location = window.location.toString().replace(/(&?template=[^&]+)/i, "");
				} else {
					location.reload(true);
				}
			};
			
			if($('#chartHolder').data('chartObject').chartID == 0){
				callback();
			} else {
				saveChart( callback() );
			}
			return;
		}
		
		// Get the template data
		$.get(PicBoard.baseURL + "rsrc.php?req=getFormatList", function(data){
			if(data.statusCode == 200){
				// OK - list retrieved. Now we need some UI to present this data. Go get the fragment
				var formats = data.formats;
				getFragment('switchOverlay', function(data){
					jQuery.facebox(data);
					
					$.each(formats, function(name, data){
						$('#overlayHolder').append(
							$('<input>', { 'id': name, 'type': 'radio', 'name': 'overlaySelection', 'val': name }).data(data).bind('click', function(){ showOverlayDescription( $(this) ) }),
							$('<label>', { 'for': name, 'text': " " + name }),
							$('<br>')
						);
					});
					
					// Bind the button.
					$('#switchConfirm').bind('click', function(){
						
						// Are there any unsaved changes?
						if(state.unsaved){
							// Prompt the user.
							confirmDialog = confirm(PicBoard.languageMap.lang_347_switchOverview);
							if(!confirmDialog) return;
						}
						
						// Or cells outside of the range of the template?
						
						
						// Get a list of the cells.
						activeCells = 0;
						
						visibleCells = getAllVisibleCellData();
						
						templateData = $('input[name=overlaySelection]:checked').data();
						
						// See how many are actually in use, by comparing with PicBoard.cellObj (empty cell object)/
						for(c in visibleCells){
							if( diffObject( PicBoard.cellObj, visibleCells[c]) > 0) activeCells++;
						}
						
						if( activeCells > templateData.cellCount ){
							// Prompt the user.
							confirmDialog = confirm(PicBoard.languageMap.lang_347_switchOverview);
							if(!confirmDialog) return;
						}
						
						if($('#chartHolder').data('chartObject').chartID == 0){
							
							// Do we already have a template variable in the request string?
							if(/template=/i.test( window.location.toString() )){
								window.location = window.location.toString().replace(/(template=)([^&]+)/i, "$1" + $('input[name=overlaySelection]:checked').val());
							} else {
								joinOperator = (window.location.search == "") ? "?" : "&";
								 window.location = "http://" + window.location.host + window.location.pathname + window.location.search + joinOperator + 'template=' + $('input[name=overlaySelection]:checked').val();
							}
													
						} else {
							$('#chartHolder').data('overlayObject').identifier = $('input[name=overlaySelection]:checked').val();
							
							jQuery.facebox.close();
							
							saveChart(function(){
								if(/id=/i.test(window.location.toString())){
									location.reload(true);
								} else {
									window.location = 'builder?id=' + $('#chartHolder').data('chartObject').chartID;
								}
							});
						
						}
						
					});
				});
			} else {
				alert(data.message);
			}
		}, 'json');
	});
}

function showOverlayDescription(overlay){
	var overlayData = $(overlay).data();
	
	
	var outputString = 
	"Cells: " + overlayData.cellCount + "<br />" +
	"Rows: " + overlayData.rows + "<br />" +
	"Columns: " + overlayData.columns + "<br />" +
	"Description: " + String(overlayData.description) + "<br />" +
	"Devices: ";
	
	$('#overlayDescription').html( "" );
	
	var deviceString = "";
	
	for(o in overlayData.devices){
		deviceString += overlayData.devices[o].name + ", ";
		$('#overlayDescription').append(
			$('<img>', { src: PicBoard.baseURL + "images/devices/" + overlayData.devices[o].icon, 'style': 'float:right', alt: overlayData.devices[o].name })
		);	
	}
	
	deviceString = deviceString.substring(0, deviceString.length - 2);
	outputString += deviceString;
	
	$('#overlayDescription').append( outputString );
}

function retrieveTranslations(){
	// Retrieve the language strings we are going to need.
	map = [	'lang_249_deleteChart', 'lang_250_chartUnique', 'lang_251_searching', 'lang_252_copyTool', 'lang_253_eraseTool', 'lang_248_noResults', 'lang_254_savingChart', 'lang_223_nextPage', 
			'lang_185_previousPage', 'lang_266_zoomIn', 'lang_267_zoomOut', 'lang_274_noGroupOption', 'lang_282_unsavedWork', 'lang_285_donationConfirmation', 'lang_286_chartSaved', 'lang_287_copySaved', 'lang_347_switchOverview'];
	
	PicBoard.getTranslations(map, function(){});
}

function setDroppable(){
	bindCellDroppable('.cell');
	
	bindCellDraggable('.cell');

}

function bindCellDroppable(target){
	$(target).droppable({
		accept: '.searchResult, .cell',
		activeClass: 'cellHover',
		drop: function(event, ui) {
			if(ui.draggable.hasClass('cell')){
				duplicateCell(ui.draggable, this);
			} else {
				copyImageToCell(ui.draggable, this);
			}
			
			if($(this).find('embed').length > 0){
				bindEmbedLoad();
			}
		
		}
	});
}

function bindCellDraggable(target){
	// Make the target draggable.
	$(target).draggable({
		revert: true,
		helper: 'clone',
		opacity: 0.7,
		handle: '#dragHandle',
		containment: "#cellHolder",
		iframeFix: true,

		start: function(event, ui){
			ui.helper.children('#dragHandle').remove();
			dragDrop.dragging = true;
			$('#dragHandle').hide();
		},
		stop: function(event, ui){
			dragDrop.dragging = false;
			$('#dragHandle').remove();
		}
	});
	
	
}

function duplicateCell(source, target){

	// Check to see if a cell has cell_s class. If it does the other cell must also.
	// If there is a mismatch we must clone using a different method
	var sourceSmall = $(source).hasClass('cell_s');
	var targetSmall = $(target).hasClass('cell_s');


	oldGridRef = $(target).data('cellObject').gridRef;
	sourceData = $(source).data('cellObject');
	//imgData = $(source).find('img, embed').data('originalDimensions');
	oldId = $(target).attr('id');
	
	var newCell = source.clone();
	newCell.attr('id', oldId);
	newCell.find('.cimg').attr('id', oldId + "img");
	newCell.find('.ctext, .ctbottom').attr('id', oldId + "text");

	newCell.find('.ui-resizable-handle').remove();
	
	if(targetSmall && !sourceSmall) $(newCell).addClass('cell_s');
	if(!targetSmall && sourceSmall) $(newCell).removeClass('cell_s');
	
	$(source).find('#dragHandle').remove();
	
	$(newCell).data('cellObject', sourceData);
	$(newCell).data('cellObject').gridRef = oldGridRef;
	
	var i = $(newCell).find('img, embed');
	var s = i.attr('src');
	i.attr('src', s.replace(/(\d+)$/, randID() ) );
	//i.data('originalDimensions', imgData);
	
	$(target).replaceWith(newCell);
	
	newCell = $('#' + oldId);

	resizeCimg(newCell);
	
	// Set up bindings
	bindCellDraggable(newCell);
	bindCellDroppable(newCell);
	
	attachSelectableCellsWithSelector(newCell.children(), null);
	bindEmbedLoad();
	
	attachResizeWithSelector(newCell);
	
	attachRenameTextWithSelector(newCell.find('.ctext, .ctbottom'));
	
	selectCell(newCell);
	$(newCell).disableSelection();
}

function setUpEditor(){

	retrieveTranslations();


	// Copy chart object
	copyChartObject();
	
	// Copy cell objects into all the cells.
	copyCellObjects();
	
	setupToolbar();

	if(viewerOnly){
		$('#PBEditTitle').button('disable');
	} else {
		// Make the searchResult's draggable, revert property resets the image back to original position when dropped regardless. This can be changed if we want to get it to disapear, for example.
		$(".searchResult").draggable({ revert: true });
		
		
		// Number of 'active' cells. Defaults to 6
		properties.activeCells = 6;
		
		state.unsaved = false;
		
		if(/template=/i.test(window.location.search) || $('#chartHolder').data('overlayObject').identifier != "defaultFormat"){
			state.overlayMode = true;
		}
		
		attachResize();
		
		attachRenameText();
		
		attachRenameChart();
		
		attachOverlayButton();
		
		// This feature has been removed
		//setupChartLocking();
		
		attachSelectables();		
		
		setupSearching();
		
		setupCellNavLinks();

		if($.browser.msie == true) ieOverflowFixer();

		setDroppable();
		
		$('.cell').live('mousemove', function(){ displayDragHandle($(this)); });
		$('.cell').disableSelection();
		
		$('#cellHolder').css({ 'padding': 'none' });

	}
}

jQuery.fn.swap = function(b){ 
    b = jQuery(b)[0]; 
    var a = this[0]; 
    var t = a.parentNode.insertBefore(document.createTextNode(''), a); 
    b.parentNode.insertBefore(a, b); 
    t.parentNode.insertBefore(b, t); 
    t.parentNode.removeChild(t); 
    return this; 
};

// Queue setUpEditor() to run when the document is ready.
PicBoard.addLoadEvent( "setUpEditor()" );
// Load the font picker
if(viewerOnly == false) PicBoard.addLoadEvent( "getFragment('fontPicker', function(data){ PicBoard.fontTemplate = data; });" );
if(viewerOnly == false) PicBoard.addLoadEvent( "getFragment('fontOptions', function(data){ PicBoard.fontOptions = data; });" );

// Add event listener for user navigating away from page. We'll warn if they have unsaved work.
if(viewerOnly == false) PicBoard.addLoadEvent( "$(window).bind('beforeunload', function(){ return checkForUnsavedWork(); });" );